/********************************************************************
 *                                                                  *
 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
 *                                                                  *
 * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002             *
 * by the XIPHOPHORUS Company http://www.xiph.org/                  *
 *                                                                  *
 ********************************************************************

 function: single-block PCM synthesis
 last mod: $Id: synthesis.c,v 1.30 2003/08/18 05:34:01 xiphmont Exp $

 ********************************************************************/

#include <stdio.h>
#include "../ogg/ogg.h"
#include "../vorbis/codec.h"
#include "codec_internal.h"
#include "registry.h"
#include "misc.h"
#include "os.h"

int vorbis_synthesis( vorbis_block* vb, ogg_packet* op )
{
	vorbis_dsp_state*     vd = vb->vd;
	private_state*        b = vd->backend_state;
	vorbis_info*          vi = vd->vi;
	codec_setup_info*     ci = vi->codec_setup;
	oggpack_buffer*       opb = &vb->opb;
	int                   type, mode, i;

	/* first things first.  Make sure decode is ready */
	_vorbis_block_ripcord( vb );
	oggpack_readinit( opb, op->packet, op->bytes );

	/* Check the packet type */
	if( oggpack_read( opb, 1 ) != 0 )
	{
		/* Oops.  This is not an audio data packet */
		return( OV_ENOTAUDIO );
	}

	/* read our mode and pre/post windowsize */
	mode = oggpack_read( opb, b->modebits );
	if( mode == -1 )
	{
		return( OV_EBADPACKET );
	}

	vb->mode = mode;
	vb->W = ci->mode_param[mode]->blockflag;
	if( vb->W )
	{

		/* this doesn;t get mapped through mode selection as it's used
		   only for window selection */
		vb->lW = oggpack_read( opb, 1 );
		vb->nW = oggpack_read( opb, 1 );
		if( vb->nW == -1 )
		{
			return( OV_EBADPACKET );
		}
	}
	else
	{
		vb->lW = 0;
		vb->nW = 0;
	}

	/* more setup */
	vb->granulepos = op->granulepos;
	vb->sequence = op->packetno;
	vb->eofflag = op->e_o_s;

	/* alloc pcm passback storage */
	vb->pcmend = ci->blocksizes[vb->W];
	vb->pcm = _vorbis_block_alloc( vb, sizeof( *vb->pcm ) * vi->channels );
	for( i = 0; i < vi->channels; i++ )
	{
		vb->pcm[i] = _vorbis_block_alloc( vb, vb->pcmend * sizeof( *vb->pcm[i] ) );
	}

	/* unpack_header enforces range checking */
	type = ci->map_type[ci->mode_param[mode]->mapping];

	return( _mapping_P[type]->inverse( vb, ci->map_param[ci->mode_param[mode]->
									   mapping] ) );
}

/* used to track pcm position without actually performing decode.
   Useful for sequential 'fast forward' */
int vorbis_synthesis_trackonly( vorbis_block* vb, ogg_packet* op )
{
	vorbis_dsp_state*     vd = vb->vd;
	private_state*        b = vd->backend_state;
	vorbis_info*          vi = vd->vi;
	codec_setup_info*     ci = vi->codec_setup;
	oggpack_buffer*       opb = &vb->opb;
	int                   mode;

	/* first things first.  Make sure decode is ready */
	_vorbis_block_ripcord( vb );
	oggpack_readinit( opb, op->packet, op->bytes );

	/* Check the packet type */
	if( oggpack_read( opb, 1 ) != 0 )
	{
		/* Oops.  This is not an audio data packet */
		return( OV_ENOTAUDIO );
	}

	/* read our mode and pre/post windowsize */
	mode = oggpack_read( opb, b->modebits );
	if( mode == -1 )
	{
		return( OV_EBADPACKET );
	}

	vb->mode = mode;
	vb->W = ci->mode_param[mode]->blockflag;
	if( vb->W )
	{
		vb->lW = oggpack_read( opb, 1 );
		vb->nW = oggpack_read( opb, 1 );
		if( vb->nW == -1 )
		{
			return( OV_EBADPACKET );
		}
	}
	else
	{
		vb->lW = 0;
		vb->nW = 0;
	}

	/* more setup */
	vb->granulepos = op->granulepos;
	vb->sequence = op->packetno;
	vb->eofflag = op->e_o_s;

	/* no pcm */
	vb->pcmend = 0;
	vb->pcm = NULL;

	return( 0 );
}

long vorbis_packet_blocksize( vorbis_info* vi, ogg_packet* op )
{
	codec_setup_info*     ci = vi->codec_setup;
	oggpack_buffer       opb;
	int                  mode;

	oggpack_readinit( &opb, op->packet, op->bytes );

	/* Check the packet type */
	if( oggpack_read( &opb, 1 ) != 0 )
	{
		/* Oops.  This is not an audio data packet */
		return( OV_ENOTAUDIO );
	}

	{
		int modebits = 0;
		int v = ci->modes;
		while( v > 1 )
		{
			modebits++;
			v >>= 1;
		}

		/* read our mode and pre/post windowsize */
		mode = oggpack_read( &opb, modebits );
	}
	if( mode == -1 )
	{
		return( OV_EBADPACKET );
	}
	return( ci->blocksizes[ci->mode_param[mode]->blockflag] );
}

int vorbis_synthesis_halfrate( vorbis_info* vi, int flag )
{
	/* set / clear half-sample-rate mode */
	codec_setup_info*     ci = vi->codec_setup;

	/* right now, our MDCT can't handle < 64 sample windows. */
	if( ci->blocksizes[0] <= 64 && flag )
	{
		return -1;
	}
	ci->halfrate_flag = ( flag ? 1 : 0 );
	return 0;
}

int vorbis_synthesis_halfrate_p( vorbis_info* vi )
{
	codec_setup_info*     ci = vi->codec_setup;
	return ci->halfrate_flag;
}


